package xset

import (
	"maps"
	"slices"
)

type Set[T comparable] struct {
	values map[T]int
}

// New 返回一个Set对象，非线程安全
// Set对象是值的集合，你可以按照插入的顺序迭代它的元素。Set 中的元素只会出现一次，即 Set 中的元素是唯一的。
func New[T comparable](v ...T) *Set[T] {
	s := &Set[T]{
		values: make(map[T]int),
	}

	s.Add(v...)
	return s
}

// Add 如果 Set 对象中没有具有相同值的元素，则 add() 方法将插入一个具有指定值的新元素到 Set 对象中。
func (s *Set[T]) Add(in ...T) {
	for _, v := range in {
		if s.Has(v) {
			continue
		}
		s.values[v] = len(s.values)
	}
}

// Delete 从 Set 对象中删除指定的值（如果该值在 Set 中）。
func (s *Set[T]) Delete(in ...T) {
	f := func(v T) {
		index, ok := s.values[v]
		if !ok {
			return
		}

		delete(s.values, v)
		for k, i := range s.values {
			if i > index {
				s.values[k] = i - 1
			}
		}
	}
	for _, v := range in {
		f(v)
	}
}

// Replace 替换指定的值
func (s *Set[T]) Replace(old, new T) {
	index, ok := s.values[old]
	if !ok {
		return
	}

	s.values[new] = index
}

// Clear 清空Set中的所有值
func (s *Set[T]) Clear() {
	maps.DeleteFunc(s.values, func(k T, v int) bool {
		return true
	})
}

// Keys 同Values
func (s *Set[T]) Keys() []T {
	return s.Values()
}

// Values 返回Set对象中的所有值
func (s *Set[T]) Values() []T {
	values := make([]T, 0)
	indexes := make([]int, 0)
	tmp := make(map[int]T)
	for v, index := range s.values {
		indexes = append(indexes, index)
		tmp[index] = v
	}
	slices.Sort(indexes)
	for _, i := range indexes {
		values = append(values, tmp[i])
	}

	return values
}

// Each 对 Set 对象中的每个值按插入顺序执行一次提供的函数。
func (s *Set[T]) Each(f func(index int, value T)) {
	values := s.Values()
	for index, v := range values {
		f(index, v)
	}
}

// Index 返回Set对象中v的索引
// 默认为-1
func (s *Set[T]) Index(v T) int {
	index, ok := s.values[v]
	if !ok {
		index = -1
	}
	return index
}

// Find 对Set对象中的元素遍历执行提供的函数，函数值为true时，返回元素的索引及值
// 默认返回 -1及nil
func (s *Set[T]) Find(f func(index int, value T) bool) (i int, t T) {
	values := s.Values()
	for index, v := range values {
		if f(index, v) {
			return index, v
		}
	}

	i = -1
	return
}

// Len 返回Set对象中元素个数
func (s *Set[T]) Len() int {
	return len(s.values)
}

// Has 返回一个布尔值来指示对应的值是否存在于 Set 对象中
func (s *Set[T]) Has(v T) bool {
	_, ok := s.values[v]
	return ok
}
